/***************************************************************************/
/*                                                                         */
/*  ftpatent.c                                                             */
/*                                                                         */
/*    FreeType API for checking patented TrueType bytecode instructions    */
/*    (body).                                                              */
/*                                                                         */
/*  Copyright 2007, 2008, 2010 by David Turner.                            */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/

#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_TRUETYPE_TAGS_H
#include FT_INTERNAL_OBJECTS_H
#include FT_INTERNAL_STREAM_H
#include FT_SERVICE_SFNT_H
#include FT_SERVICE_TRUETYPE_GLYF_H


static FT_Bool
_tt_check_patents_in_range(FT_Stream stream,
                           FT_ULong size)
{
    FT_Bool result = FALSE;
    FT_Error error;
    FT_Bytes p, end;


    if (FT_FRAME_ENTER(size))
        return 0;

    p = stream->cursor;
    end = p + size;

    while (p < end)
    {
        switch (p[0])
        {
            case 0x06: /* SPvTL // */
            case 0x07: /* SPvTL +  */
            case 0x08: /* SFvTL // */
            case 0x09: /* SFvTL +  */
            case 0x0A: /* SPvFS    */
            case 0x0B: /* SFvFS    */
                result = TRUE;
                goto Exit;

            case 0x40:
                if (p + 1 >= end)
                    goto Exit;

                p += p[1] + 2;
                break;

            case 0x41:
                if (p + 1 >= end)
                    goto Exit;

                p += p[1] * 2 + 2;
                break;

            case 0x71: /* DELTAP2 */
            case 0x72: /* DELTAP3 */
            case 0x73: /* DELTAC0 */
            case 0x74: /* DELTAC1 */
            case 0x75: /* DELTAC2 */
                result = TRUE;
                goto Exit;

            case 0xB0:
            case 0xB1:
            case 0xB2:
            case 0xB3:
            case 0xB4:
            case 0xB5:
            case 0xB6:
            case 0xB7:
                p += (p[0] - 0xB0) + 2;
                break;

            case 0xB8:
            case 0xB9:
            case 0xBA:
            case 0xBB:
            case 0xBC:
            case 0xBD:
            case 0xBE:
            case 0xBF:
                p += (p[0] - 0xB8) * 2 + 3;
                break;

            default:
                p += 1;
                break;
        }
    }

Exit:
    FT_UNUSED(error);
    FT_FRAME_EXIT();
    return result;
}


static FT_Bool
_tt_check_patents_in_table(FT_Face face,
                           FT_ULong tag)
{
    FT_Stream stream = face->stream;
    FT_Error error = FT_Err_Ok;
    FT_Service_SFNT_Table service;
    FT_Bool result = FALSE;


    FT_FACE_FIND_SERVICE(face, service, SFNT_TABLE);

    if (service)
    {
        FT_UInt i = 0;
        FT_ULong tag_i = 0, offset_i = 0, length_i = 0;


        for (i = 0; !error && tag_i != tag; i++)
            error = service->table_info(face, i,
                                        &tag_i, &offset_i, &length_i);

        if (error ||
            FT_STREAM_SEEK(offset_i))
            goto Exit;

        result = _tt_check_patents_in_range(stream, length_i);
    }

Exit:
    return result;
}


static FT_Bool
_tt_face_check_patents(FT_Face face)
{
    FT_Stream stream = face->stream;
    FT_UInt gindex;
    FT_Error error;
    FT_Bool result;

    FT_Service_TTGlyf service;


    result = _tt_check_patents_in_table(face, TTAG_fpgm);
    if (result)
        goto Exit;

    result = _tt_check_patents_in_table(face, TTAG_prep);
    if (result)
        goto Exit;

    FT_FACE_FIND_SERVICE(face, service, TT_GLYF);
    if (service == NULL)
        goto Exit;

    for (gindex = 0; gindex < (FT_UInt)face->num_glyphs; gindex++)
    {
        FT_ULong offset, num_ins, size;
        FT_Int num_contours;


        offset = service->get_location(face, gindex, &size);
        if (size == 0)
            continue;

        if (FT_STREAM_SEEK(offset) ||
            FT_READ_SHORT(num_contours))
            continue;

        if (num_contours >= 0)  /* simple glyph */
        {
            if (FT_STREAM_SKIP(8 + num_contours * 2))
                continue;
        }
        else /* compound glyph */
        {
            FT_Bool has_instr = 0;


            if (FT_STREAM_SKIP(8))
                continue;

            /* now read each component */
            for (;;)
            {
                FT_UInt flags, toskip;


                if (FT_READ_USHORT(flags))
                    break;

                toskip = 2 + 1 + 1;

                if ((flags & (1 << 0)) != 0)       /* ARGS_ARE_WORDS */
                    toskip += 2;

                if ((flags & (1 << 3)) != 0)       /* WE_HAVE_A_SCALE */
                    toskip += 2;
                else if ((flags & (1 << 6)) != 0)  /* WE_HAVE_X_Y_SCALE */
                    toskip += 4;
                else if ((flags & (1 << 7)) != 0)  /* WE_HAVE_A_2x2 */
                    toskip += 8;

                if ((flags & (1 << 8)) != 0)       /* WE_HAVE_INSTRUCTIONS */
                    has_instr = 1;

                if (FT_STREAM_SKIP(toskip))
                    goto NextGlyph;

                if ((flags & (1 << 5)) == 0)       /* MORE_COMPONENTS */
                    break;
            }

            if (!has_instr)
                goto NextGlyph;
        }

        if (FT_READ_USHORT(num_ins))
            continue;

        result = _tt_check_patents_in_range(stream, num_ins);
        if (result)
            goto Exit;

NextGlyph:
        ;
    }

Exit:
    return result;
}


/* documentation is in freetype.h */

FT_EXPORT_DEF(FT_Bool)
FT_Face_CheckTrueTypePatents(FT_Face face)
{
    FT_Bool result = FALSE;


    if (face && FT_IS_SFNT(face))
        result = _tt_face_check_patents(face);

    return result;
}


/* documentation is in freetype.h */

FT_EXPORT_DEF(FT_Bool)
FT_Face_SetUnpatentedHinting(FT_Face face,
                             FT_Bool value)
{
    FT_Bool result = FALSE;


    #if defined (TT_CONFIG_OPTION_UNPATENTED_HINTING) && \
    !defined (TT_CONFIG_OPTION_BYTECODE_INTEPRETER)
    if (face && FT_IS_SFNT(face))
    {
        result = !face->internal->ignore_unpatented_hinter;
        face->internal->ignore_unpatented_hinter = !value;
    }
    #else
    FT_UNUSED(face);
    FT_UNUSED(value);
    #endif

    return result;
}

/* END */